﻿using System;
using System.Collections.Generic;
using GameFramework.Battle.Core;
using Gameplay.PVE.Utils;
using ImageEffects;
using RootMotion.FinalIK;
using UnityEngine;
using Yoozoo.Libs;
using Yoozoo.Managers;
using Random = System.Random;

namespace Gameplay.PVE.Survivor
{
    public class MeshModelRenderer: ModelRenderer
    {
        private class RendererMatData: IClass
        {
            public Renderer renderer;
            public void OnReset()
            {
                renderer = null;
            }
        }
        
        private Animator animator;
        private GameObject gameObject;
        private Transform transform;

        private int topLayerIndex;
        private LookAtComponent lookAtComponent;
        private BattleUnit unit;
        private ModelHitPoint hitPoint;
        private SurvivorLightElement lightElement;
        private bool isFullBody;

        private MeshBaker baker;
        
        public void Initialize(BattleUnit unit,GameObject gameObject)
        {
            this.unit = unit;
            this.gameObject = gameObject;
            transform = gameObject.transform;
            animator = gameObject.GetComponent<Animator>();
            animator.speed = speed;
            isFullBody = false;
            if (unit.config != null && unit.config.is_full_body > 0)
            {
                isFullBody = true;
            }
            SetBool("IsKnocking",false);
            SetTrigger("Born");
            SetFloat("BattleState",0);
            SetFloat("Speed",0);
            topLayerIndex = animator.GetLayerIndex("Top");
            if (topLayerIndex >= 0)
            {
                animator.SetLayerWeight(topLayerIndex,isFullBody ? 0 : 1);
            }
            lookAtComponent = gameObject.GetComponent<LookAtComponent>();
            hitPoint = gameObject.GetComponent<ModelHitPoint>();
            if (hitPoint)
            {
                if (hitPoint.gunPoints != null && hitPoint.gunPoints.Count > 0)
                {
                    unit.gunPoint = hitPoint.gunPoints[0];
                }
                unit.gunPoints = hitPoint.gunPoints;
                unit.hitPoints = hitPoint.hitPoints;
            }
            InitMeshRenderer(gameObject);
             
            lightElement = gameObject.GetComponentInChildren<SurvivorLightElement>();
            if (lightElement)
            {
                lightElement.SetPropertyBlock(propertyBlock);
            }
            RefreshLightElement();
            isAimingChanged = true;
        }
        
        private List<RendererMatData> renderMats = new List<RendererMatData>();
        private static readonly int MaskAmount = Shader.PropertyToID("_MaskAmount");
        private static readonly int MaskColor = Shader.PropertyToID("_MaskColor");
        private static readonly int ShadowHeight = Shader.PropertyToID("_ShadowHeight");
        private static readonly int ShadowOffset = Shader.PropertyToID("_ShadowOffset");

        private bool propertyDirty = false;
        private MaterialPropertyBlock propertyBlock;
        
        public void HideWeapon(int index)
        {
            //hitPoint?.HideWeapon(index);
        }

        public void ShowPhantom()
        {
            if (baker == null)
            {
                baker = ClassManager.Get<MeshBaker>();
                baker.Initialize(gameObject);
            }
            baker.Bake();
        }
        
        private void InitMeshRenderer(GameObject gameObject)
        {
            propertyBlock = new MaterialPropertyBlock();
            var meshRender = gameObject.GetComponentsInChildren<SkinnedMeshRenderer>();
            if (meshRender.Length > 0)
            {
                for (int i = 0; i < meshRender.Length; i++)
                {
                    var obj = meshRender[i].gameObject;
                    meshRender[i].GetPropertyBlock(propertyBlock);
                    propertyBlock.SetFloat(MaskAmount,0);
                    var renderData = ClassManager.Get<RendererMatData>();
                    renderData.renderer = meshRender[i];
                    renderMats.Add(renderData);
                }
            }
            UpdateShadowOffset();
        }

        public override void SetFloat(string name, float value)
        {
            animator.SetFloat(name,value);
        }

        public override void SetBool(string name, bool value)
        {
            animator.SetBool(name,value);
        }
        
        public override void SetTrigger(string name)
        {
            animator.SetTrigger(name);
        }

        public override void Play(string name,bool isFullBody = false,bool resetTransition = true)
        {
            if (BattleDebug.enableLog)
            {
                BattleDebug.Log(unit.unitId + " PlayAnimation " + name + "  IsFullBody " + isFullBody + " IsKnocking " + animator.GetBool("IsKnocking"));
            }
            animator.SetTrigger(name);
            if (this.isFullBody)
            {
                return;
            }

            if (topLayerIndex >= 0)
            {
                if (isFullBody)
                {
                    animator.SetLayerWeight(topLayerIndex,0);
               
                }
                else
                {
                    animator.SetLayerWeight(topLayerIndex,1);
                }
            }
        }
        
        public override void SetFlashAmount(float amount)
        {
            propertyBlock.SetFloat(MaskAmount,amount);
            propertyDirty = true;
        }
        
        private bool isAimingChanged;
        private bool isAiming;
        private Transform aimTarget;
        
        private float speedChangeTime = 0.1f;
        private float targetSpeed;
        private float currentSpeed;

        private float targetVerticalSpeed;
        private float currentVerticalSpeed;
        private float targetHorizontalSpeed;
        private float currentHorizontalSpeed;

        private float currentBattleStateValue;
        private float targetBattleStateValue;

        private Vector3 forward;

        private float lastY = -9999f;
        public override void Update()
        {
            base.Update();
            if (lastY != unit.Position.y)
            {
                SetShadowHeight(unit.Position.y + 0.03f);
                lastY = unit.Position.y;
            }
            UpdateProperty();
            baker?.Update();
        }

        private void UpdateProperty()
        {
            if (propertyDirty)
            {
                for (int i = 0; i < renderMats.Count; i++)
                {
                    renderMats[i].renderer.SetPropertyBlock(propertyBlock);
                }
                propertyDirty = false;
            }
        }
        
        private void SetShadowHeight(float height)
        {
            propertyBlock.SetFloat(ShadowHeight,height);
            propertyDirty = true;
        }

        public void UpdateShadowOffset()
        {
            propertyBlock.SetVector(ShadowOffset,Vector4.zero);
            propertyDirty = true;
        }
        
        protected override void UpdateAiming()
        {
            var valueChanged = UpdateValueChange(ref currentSpeed,ref targetSpeed,1 / speedChangeTime);
            if (valueChanged || isAimingChanged)
            {
                /*if (isAiming)
                {
                    SetFloat("Speed",currentSpeed / 2);
                }
                else*/
                {
                    SetFloat("Speed",currentSpeed / 2);
                }
                isAimingChanged = false;
            }
            //如果不在瞄准中，则向前
            if (!isAiming)
            {
                targetVerticalSpeed = 1;
                targetHorizontalSpeed = 0;
                if (unit.Forward != default)
                {
                    transform.forward = unit.Forward;
                }
            }
            else
            {
                //获取目标向量
                var targetPosition = aimTarget.position;
                var targetDir = targetPosition - transform.position;
                targetDir.y = 0;
                var targetDirAngle = PveUtils.GetAngle(unit.Forward, targetDir);
                var fixedForward = Quaternion.AngleAxis(-targetDirAngle, Vector3.up) * Vector3.forward;
                fixedForward = fixedForward.normalized;
                //UnityEngine.Debug.LogError(targetDirAngle + " " + fixedForward + " " + unit.Forward);
                targetVerticalSpeed = fixedForward.z;
                targetHorizontalSpeed = fixedForward.x;
                //UnityEngine.Debug.LogError("targetVerticalSpeed:" + targetVerticalSpeed + " targetHorizontalSpeed:" + targetHorizontalSpeed + " targetDir:" + targetDir + " fixedForward:" + fixedForward);
                /*if (targetVerticalSpeed < 0)
                {
                    targetVerticalSpeed = -1;
                    targetHorizontalSpeed = 0;
                    gameObject.Forward = -unit.Forward;
                    forward = unit.Forward;
                    SetFloat("WalkH",targetHorizontalSpeed);
                    SetFloat("WalkV",targetVerticalSpeed);
                }
                else*/
                {
                    transform.forward = targetDir;
                    //forward = targetDir;
                }
            }
            valueChanged = UpdateValueChange(ref currentVerticalSpeed,ref targetVerticalSpeed,1 / speedChangeTime);
            if (valueChanged)
            {
                SetFloat("WalkV",currentVerticalSpeed);
            }
            valueChanged = UpdateValueChange(ref currentHorizontalSpeed,ref targetHorizontalSpeed,1 / speedChangeTime);
            if (valueChanged)
            {
                SetFloat("WalkH",currentHorizontalSpeed);
            }
        }

        protected override void UpdateBattleState()
        {
            if (unit.behaviorState == EBehaviorState.Battle)
            {
                targetBattleStateValue = 1;
            }
            else
            {
                targetBattleStateValue = 0;
            }
            var valueChanged = UpdateValueChange(ref currentBattleStateValue,ref targetBattleStateValue,1 / speedChangeTime);
            if (valueChanged)
            {
                SetFloat("BattleState",currentBattleStateValue);
            }
        }

        public void StartAim(Transform target,bool useStartPoint = true)
        {
            isAiming = true;
            aimTarget = target;
            isAimingChanged = true;
            lookAtComponent?.StartAim(target,useStartPoint);
        }
        
        public void EndAim()
        {
            isAiming = false;
            aimTarget = null;
            isAimingChanged = true;
            lookAtComponent?.EndAim();
        }

        public void RefreshLightElement()
        {
            if (lightElement)
            {
                lightElement.controllerMark = BattleConstValue.LightElementMark;
                lightElement.Refresh();
            }
        }

        public void SetSpeed(float speed,bool immediate = false)
        {
            targetSpeed = speed;
            if (immediate)
            {
                currentSpeed = speed;
                SetFloat("Speed",currentSpeed);
            }
        }

        public override void OnDead()
        {
            if (animator)
            {
                if (topLayerIndex >= 0)
                {
                    animator.SetLayerWeight(topLayerIndex,0);
                }
                animator.SetFloat("Speed",0);
            }

            if (propertyDirty)
            {
                UpdateProperty();
            }
        }

        public override void SetAnimatorSpeed(float speed)
        {
            this.speed = speed;
            if (animator)
            {
                animator.speed = speed;
            }
        }

        public override void Dispose()
        {
            base.Dispose();
            if (baker != null)
            {
                baker.ClearShadow();
                ClassManager.Free(baker);
                baker = null;
            }
            if (animator)
            {
                animator.SetFloat("Speed",0);
                animator = null;
            }
            if (propertyBlock != null)
            {
                propertyBlock.Clear();
                propertyBlock = null;
            }
            if (renderMats != null)
            {
                for (int i = 0; i < renderMats.Count; i++)
                {
                    ClassManager.Free(renderMats[i]);
                }
                renderMats.Clear();
            }
            currentSpeed = 0;
            targetSpeed = 0;
            currentVerticalSpeed = 0;
            targetVerticalSpeed = 0;
            currentHorizontalSpeed = 0;
            targetHorizontalSpeed = 0;
            lightElement = null;
            animator = null;
            isAiming = false;
            aimTarget = null;
            lookAtComponent = null;
            hitPoint = null;
            speed = 1;
        }
    }
}
